package dalama;

import java.awt.Color;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import kdtree.KdTree;
import kdtree.MaxHeap;
import robocode.AdvancedRobot;
import robocode.RobotDeathEvent;
import robocode.Rules;
import robocode.ScannedRobotEvent;
import robocode.util.Utils;
import utils.RoboUtils;
import utils.RobotSituation;
import utils.Wave;
import utils.WeightedSquareEuclideanDistance;

public class DamalaMelee extends AdvancedRobot {

	// MINIMUM RISK FUNCTION WEIGHTS

	static double DISTANCE_WEIGHT = 1.2;

	static double CENTER_DISTANCE_WEIGHT = 1.5;

	// IT MUST BE ALWAYS NEGATIVE
	static double AM_I_THE_NEAREST_PENALTY = -100;

	static double DISTANCE_TO_TRAVEL = 0.8;

	static double SAME_PLACE_PENALTY = -2;

	// *******************************************

	private HashMap<String, Enemy> enemies;
	private Point2D.Double myPosition;
	private java.awt.geom.Rectangle2D.Double fieldRect;
	private long timeSameSlice;
	private Point previousSlice;
	double bulletPower = 0.1;

	// *******************gun**********************
	private KdTree<Integer> situationsKDTree = new KdTree<>(Constants.dimensions);
	private ArrayList<RobotSituation> situations = new ArrayList<>();
	private static ArrayList<Integer> relatedClasses = new ArrayList<>();
	private int direction;
	private static BufferedReader br;
	private boolean switched1vs1 = false;

	private void initializeStuff() {

		setAdjustGunForRobotTurn(true);
		setAdjustRadarForGunTurn(true);

		enemies = new HashMap<String, Enemy>();
		myPosition = new Point2D.Double();
		fieldRect = new Rectangle2D.Double(100, 100, getBattleFieldWidth() - 100, getBattleFieldHeight() - 100);
		timeSameSlice = 0;
		previousSlice = new Point();

		try {
			if (br == null) {
				br = new BufferedReader(new FileReader(getDataDirectory().getPath()+"/parameters.dat"));
				System.out.println("PRIMA    " +DISTANCE_WEIGHT);
				loadPriorKnowledge();
				System.out.println("DOPO     " +DISTANCE_WEIGHT);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	
	public void loadPriorKnowledge() {
		try {
			 BufferedReader br = new BufferedReader(new FileReader(getDataDirectory().getPath()+"/parameters.dat"));
			    try {
			        StringBuilder sb = new StringBuilder();
			        String line = br.readLine();
			        String[] parameters = line.split(";");
					
						DISTANCE_WEIGHT = Double.parseDouble(parameters[0]);
						CENTER_DISTANCE_WEIGHT = Double.parseDouble(parameters[1]);
						DISTANCE_TO_TRAVEL =  Double.parseDouble(parameters[3]);
						SAME_PLACE_PENALTY =  Double.parseDouble(parameters[4])* (-1);
						AM_I_THE_NEAREST_PENALTY =  Double.parseDouble(parameters[2])* (-100);
			        	
			        
			    } finally {
			        br.close();
			    }
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}	
	
	
	@Override
	public void run() {

		initializeStuff();
		decayAppereance();
		while (true) {
			myPosition.setLocation(getX(), getY());

			if (fromCoordinatesToSlices(myPosition).equals(previousSlice))
				timeSameSlice++;
			else {
				timeSameSlice = 0;
				previousSlice = fromCoordinatesToSlices(myPosition);
			}

			doRadar();
			doGun();

			// tick++;
			// if (tick % 15 == 0)
			if (getTurnRemaining() == 0 && getDistanceRemaining() == 0)
				goTo(getMinimumRiskPoint());

			execute();
		}

	}

	private Point fromCoordinatesToSlices(Point2D.Double point) {

		double sliceWidth = (getBattleFieldWidth() / Constants.X_FIELD_SLICES);
		double sliceHeight = (getBattleFieldHeight() / Constants.Y_FIELD_SLICES);

		return new Point((int) (point.x / sliceWidth), (int) (point.y / sliceHeight));
	}

	private void decayAppereance() {
		setColors(Constants.bodyColor, Constants.gunColor, Constants.radarColor);

	}

	private void doGun() {

		if (enemies.size() == 0)
			return;

		if (enemies.size() == 1) {// fatto da davide
			doFirePower((Enemy) enemies.values().toArray()[0]);
			knnTargeting((Enemy) enemies.values().toArray()[0]);
			return;
		} else if (getGunTurnRemaining() == 0) {
			Enemy toTarget = getEnemyToTarget();
			doFirePower(toTarget);
			if (enemies.size() >= Constants.SEMILINEAR_THRESHOLD) {
				semilinearTargeting(toTarget);
			} else {
				guessFactorTargeting();
			}
		}
		if (getGunHeat() == 0) {
			setFireBullet(bulletPower);
		}

	}

	private void knnTargeting(Enemy enemy) {
		if (situationsKDTree.size() > Constants.KNNThreshold) {
			double[] GF = new double[Constants.KNN_NUM_NEIGHBORS];
			double[] firingAngles = new double[Constants.KNN_NUM_NEIGHBORS];
			RobotSituation newSituation = situations.get(situations.size() - 1);
			// MaxHeap<Integer> maxHeap =
			// situationsKDTree.findNearestNeighbors(newSituation.toKD_Key(),
			// 3,new SquareEuclideanDistanceFunction());
			MaxHeap<Integer> maxHeap = situationsKDTree.findNearestNeighbors(newSituation.toKD_Key(),
					Constants.KNN_NUM_NEIGHBORS, new WeightedSquareEuclideanDistance(Constants.dimensions,
							Constants.weights));

			// prenditi tutti i guessFactors -> trasformali in fireingAngles
			// corrispondenti
			// applica un kernel density estimator per calcolare il bestFiring
			// angle
			double guessfactor = fromIndexToGuessFactor(maxHeap.getMax(), Constants.numSliceGF);

			int cont = 0;
			while (maxHeap.size() > 0) {
				GF[cont] = fromIndexToGuessFactor(maxHeap.getMax(), Constants.numSliceGF);
				firingAngles[cont] = fromGuessFactorToFiringAngle(GF[cont], bulletPower, enemy);
				maxHeap.removeMax();
				cont++;
			}

			double bestFiringAngle = getBestFiringAngle(firingAngles, Constants.KNN_NUM_NEIGHBORS,
					newSituation.getDistance());

			// double gunAdjust = fromGuessFactorToFiringAngle(guessfactor,
			// firePower);
			double gunAdjust = bestFiringAngle;

			setTurnGunRightRadians(gunAdjust);

			if (getGunHeat() == 0 && gunAdjust < Math.atan2(9, enemy.getDistance())) {
				setFireBullet(bulletPower);
			}
		}

	}

	private void doFirePower(Enemy enemy) {
		/**
		 * selects a bullet power based on our distance away from the target
		 * */

		Point2D.Double myCurrPos = new Point2D.Double(getX(), getY());
		double min = 1;
		double max = 2.5;
		double maxDistance = Math.sqrt(Math.pow(getBattleFieldHeight() - 36, 2) + Math.pow(getBattleFieldWidth() - 36, 2));
		double distanceFromEnemy = myCurrPos.distance(enemy.getPosition());
		bulletPower = min + (max - min) * (1 - (distanceFromEnemy / maxDistance));

		if (distanceFromEnemy < Constants.MIN_DIST_ENEMY_THRESHOLD) {
			// max power
			bulletPower = 3;
		}
		if (getEnergy() < Constants.MIN_ENERGY_THRESHOLD) {
			bulletPower = 0.1;
		}

		if (Constants.getBulletDamage(bulletPower) > enemy.getEnergy()) {
			bulletPower = Constants.getBulletEnergyFromDamage(enemy.getEnergy());
		}

		if (bulletPower > 3) {
			bulletPower = 3;
		}

	}
	
	private void guessFactorTargeting() {

		Enemy minEnergyEnemy = null;
		double min = Double.MAX_VALUE;

		for (Enemy enemy : enemies.values()) {
			if (enemy.getEnergy() < min) {
				min = enemy.getEnergy();
				minEnergyEnemy = enemy;
			}
		}

		int indexMax = 0;

		for (int i = 0; i < Constants.numSliceGF; i++) {
			if (minEnergyEnemy.STAT[i] > minEnergyEnemy.STAT[indexMax]) {
				indexMax = i;
			}
		}

		double guessfactor = fromIndexToGuessFactor(indexMax, Constants.numSliceGF);

		if (getGunTurnRemaining() == 0) {
			double angle = Utils.normalRelativeAngle(Math.toRadians(getGunHeading() - minEnergyEnemy.getBearing()
					+ guessfactor));
			setTurnGunLeftRadians(angle);
		}

	}

	private double fromIndexToGuessFactor(int index, int slices) {
		double sliceSize = 2.0 / (double) slices;
		double sliceStart = index * sliceSize;
		return (sliceStart + sliceSize / 2 - 1);// center of class

	}

	private double fromGuessFactorToFiringAngle(double GF, double bullettPower, Enemy enemy) {

		double angleOffset = direction * GF * (RoboUtils.getMaxEscapeAngle(bullettPower));
		double gunAdjust = Utils.normalRelativeAngle(Math.toRadians(enemy.getBearing()) - getGunHeadingRadians()
				+ angleOffset);
		return gunAdjust;
	}

	private void processWaves(Enemy enemy) {

		// // Let's process the waves now:
		for (int i = 0; i < enemy.waves.size(); i++) {
			Wave currentWave = (Wave) enemy.waves.get(i);
			if (currentWave.checkHit(enemy.getPosition().getX(), enemy.getPosition().getY(), getTime())) {
				updateStats(currentWave, enemy);
				enemy.waves.remove(currentWave);
				i--;

			}
		}

		Wave newWave = new Wave(myPosition.x, myPosition.y, enemy.getBearing(), bulletPower,
				enemy.getDirection(), getTime());

		enemy.waves.add(newWave);
	}

	private void processWaves1vs1(Enemy enemy) {

		// find our enemy's location:
		double ex = enemy.getPosition().x;
		double ey = enemy.getPosition().y;

		// Let's process the waves now:
		for (int i = 0; i < enemy.waves.size(); i++) {
			Wave currentWave = (Wave) enemy.waves.get(i);
			if (currentWave.checkHit(ex, ey, getTime())) {
				currentWave.getRelatedSituation().setRightGessFsctor(currentWave.getRightGuessFactor());
				situationsKDTree.addPoint(currentWave.getRelatedSituation().toKD_Key(),
						fromGuessFactorToIndex(currentWave.getRightGuessFactor() + 1, Constants.numSliceGF));

				situations.add(currentWave.getRelatedSituation());
				relatedClasses.add(fromGuessFactorToIndex(currentWave.getRightGuessFactor() + 1, Constants.numSliceGF));

				enemy.waves.remove(currentWave);
				i--;
			}
		}

		RobotSituation newSituation = new RobotSituation();

		newSituation.setDistance(enemy.getDistance());
		newSituation.setBulletFlightTime(enemy.getDistance() / RoboUtils.bulletVelocity(bulletPower));
		newSituation.setTime(getTime());
		newSituation.setVelocity(enemy.getSpeed());
		newSituation.setAdvancingVelocity(enemy.getSpeed() * -1
				* Math.cos(Math.toRadians(enemy.getHeading()) - (Math.toRadians(enemy.getBearing()))));
		newSituation.setLateralVelocity(enemy.getSpeed()
				* Math.sin(Math.toRadians(enemy.getHeading()) - (Math.toRadians(enemy.getBearing()))));
		newSituation.setPower(bulletPower);
		if (situations.size() > 0) {
			double oldLatVel = situations.get(situations.size() - 1).getLateralVelocity();
			double oldAdvVel = situations.get(situations.size() - 1).getAdvancingVelocity();
			double deltaTime = getTime() - situations.get(situations.size() - 1).getTime();// currTime
			// - old
			// time
			newSituation.setLateralAcceleration(Enemy.returnAcceleration(oldLatVel, newSituation.getLateralVelocity(),
					deltaTime));
			newSituation.setAdvancingAcceleration(Enemy.returnAcceleration(oldAdvVel,
					newSituation.getAdvancingVelocity(), deltaTime));
		}

		newSituation.normalizeSituation();
		situations.add(newSituation);

		if (enemy.getSpeed() != 0) {
			if (Math.sin(Math.toRadians(enemy.getHeading()) - Math.toRadians(enemy.getBearing())) * enemy.getSpeed() < 0)
				direction = -1;
			else
				direction = 1;
		}

		Wave newWave = new Wave(getX(), getY(),Math.toRadians(enemy.getBearing()), bulletPower, direction, getTime(), newSituation);
		enemy.waves.add(newWave);

	}

	// compute the guess factor and convert it to an index on the STAT array
	public int getStatsIndex(Wave wave, Point2D.Double pos) {

		// Maximum escape angle computation
		double maxEscapeAngle = Math.asin(Rules.MAX_VELOCITY / wave.getBulletVelocity());
		// angle between the wave origin and pos
		double currentBearingRespectToWave = Math.atan2(pos.x - wave.getStartPoint().getX(), pos.y
				- wave.getStartPoint().getY());

		double offset = currentBearingRespectToWave - Math.toRadians(wave.getStartAbsBearing());

		// guess factor represent the possible gun bearing represented as a
		// fraction of the maximum escape angle
		double guessFactor = wave.getDirection() * Utils.normalRelativeAngle(offset) / maxEscapeAngle;

		if (guessFactor > 1)
			guessFactor = 1;
		else if (guessFactor < -1)
			guessFactor = -1;

		guessFactor++;

		return (int) (Constants.numSliceGF / 2 * guessFactor);
	}

	private int fromGuessFactorToIndex(double guessFactor, int slices) {
		// range GF [0,2]
		return (int) ((guessFactor) * slices) / (2);
	}

	// update STAT array when the robot is hitted by a bullet
	public void updateStats(Wave wave, Enemy enemy) {

		int index = getStatsIndex(wave, enemy.getPosition());

		// out.println("hitted!!! " + index);

		for (int i = 0; i < Constants.numSliceGF; i++) {
			enemy.STAT[i] += 1.0 / (Math.pow(index - i, 2) + 1);
		}

	}

	private void semilinearTargeting(Enemy toTarget) {

		bulletPower = Math.min(3.0, getEnergy());
		double absoluteBearing = Math.toRadians(toTarget.getBearing());
		double bulletPower = Math.min(3.0, getEnergy());
		double myX = getX();
		double myY = getY();
		double enemyX = getX() + toTarget.getDistance() * Math.sin(absoluteBearing);
		double enemyY = getY() + toTarget.getDistance() * Math.cos(absoluteBearing);
		double enemyHeading = Math.toRadians(toTarget.getHeading());
		double enemyVelocity = toTarget.getSpeed();

		double deltaTime = 0;
		double battleFieldHeight = getBattleFieldHeight(), battleFieldWidth = getBattleFieldWidth();
		double predictedX = enemyX, predictedY = enemyY;
		while ((++deltaTime) * (20.0 - 3.0 * bulletPower) < Point2D.Double.distance(myX, myY, predictedX, predictedY)) {
			predictedX += Math.sin(enemyHeading) * enemyVelocity;
			predictedY += Math.cos(enemyHeading) * enemyVelocity;
			if (predictedX < 18.0 || predictedY < 18.0 || predictedX > battleFieldWidth - 18.0
					|| predictedY > battleFieldHeight - 18.0) {
				predictedX = Math.min(Math.max(18.0, predictedX), battleFieldWidth - 18.0);
				predictedY = Math.min(Math.max(18.0, predictedY), battleFieldHeight - 18.0);
				break;
			}
		}
		double theta = Utils.normalAbsoluteAngle(Math.atan2(predictedX - getX(), predictedY - getY()));

		// setTurnRadarRightRadians(
		// Utils.normalRelativeAngle(absoluteBearing -
		// getRadarHeadingRadians()));
		setTurnGunRightRadians(Utils.normalRelativeAngle(theta - getGunHeadingRadians()));

	}

	private Enemy getEnemyToTarget() {

		double evaluation = Double.MAX_VALUE;
		double min = Double.MAX_VALUE;
		double DISTANCE_WEIGHT = 0.8;
		double ENERGY_WEIGHT = 1;
		Enemy toTarget = null;

		for (Enemy enemy : enemies.values()) {
			evaluation = enemy.getPosition().distance(myPosition) * DISTANCE_WEIGHT;
			evaluation += enemy.getEnergy() * ENERGY_WEIGHT;
			if (evaluation < min) {
				min = evaluation;
				toTarget = enemy;
			}

		}
		return toTarget;

	}

	public Point2D.Double getMinimumRiskPoint() {

		double angle;
		double distance;
		Point2D.Double point;
		double max = Double.MIN_VALUE;
		double tmp;
		Point2D.Double pointMax = new Point2D.Double(getX(), getY());

		for (int i = 0; i < Constants.NUM_POINTS; i++) {
			angle = Math.random() * 360;
			distance = Constants.MIN_GENERATION_RANGE + Math.random()
					* (Constants.MAX_GENERATION_RANGE - Constants.MIN_GENERATION_RANGE);
			point = project(myPosition, angle, distance);
			if (point.x > 100 && point.y > 100 && point.x < getBattleFieldWidth() - 100
					&& point.y < getBattleFieldHeight() - 100) {
				tmp = evaluatePoint(point);
				if (tmp > max) {
					pointMax = point;
					max = tmp;
				}
			}
		}

		getGraphics().setColor(Color.orange);
		getGraphics().fillRect((int) pointMax.x, (int) pointMax.y, 10, 10);

		return pointMax;

	}

	public double evaluatePoint(Point2D.Double point) {

		double evaluation = getAverageDistanceEnergy(point) * DISTANCE_WEIGHT;

		evaluation += getDistanceFromCenter(point) * CENTER_DISTANCE_WEIGHT;

		evaluation += getNumOfEnemiesByMinDistance(point) * AM_I_THE_NEAREST_PENALTY;

		evaluation += (Constants.MAX_GENERATION_RANGE / (myPosition.distance(point) + 1)) * DISTANCE_TO_TRAVEL;

		if (fromCoordinatesToSlices(point).equals(previousSlice))
			evaluation += timeSameSlice * SAME_PLACE_PENALTY;

		return evaluation;

	}

	public int getNumOfEnemiesByMinDistance(Point2D.Double point) {

		int enemiesNum = 0;

		for (Enemy enemy : enemies.values()) {
			boolean found = false;
			for (Enemy enemy1 : enemies.values()) {
				if (enemy == enemy1)
					continue;
				if (enemy1.getPosition().distance(enemy.getPosition()) < enemy1.getPosition().distance(myPosition)) {
					found = true;
					break;
				}
			}
			if (!found)
				enemiesNum++;

			found = false;
		}
		return enemiesNum;
	}

	public double getDistanceFromCenter(Point2D.Double point) {
		Point2D.Double center = new Point2D.Double(fieldRect.getCenterX(), fieldRect.getCenterY());
		return center.distance(myPosition);
	}

	public double getAverageDistanceEnergy(Point2D.Double point) {
		double sum = 0;
		double maxEnergy = Double.MIN_VALUE;
		for (Enemy enemy : enemies.values())
			if (enemy.getEnergy() > maxEnergy)
				maxEnergy = enemy.getEnergy();

		for (Enemy enemy : enemies.values())
			sum += enemy.getPosition().distance(point) * (enemy.getEnergy() / maxEnergy);

		return sum / enemies.size();

	}

	public void doRadar() {

		if (getRadarTurnRemaining() == 0)
			setTurnRadarLeft(360);
	}

	private void doRadar1vs1(ScannedRobotEvent e) {
		if (getRadarTurnRemaining() == 0.0 && e == null) {
			setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
		} else if (e != null) {
			double angleToEnemy = getHeadingRadians() + e.getBearingRadians();
			double radarTurn = Utils.normalRelativeAngle(angleToEnemy - getRadarHeadingRadians());
			double extraTurn = Math.min(Math.atan(30.0 / e.getDistance()), Rules.RADAR_TURN_RATE_RADIANS);
			radarTurn += (radarTurn < 0 ? -extraTurn : extraTurn);

			if (situationsKDTree.size() <= Constants.KNNThreshold) {
				// Turn the radar
				double absoluteBearing = getHeadingRadians() + e.getBearingRadians();
				setTurnGunRightRadians(robocode.util.Utils
						.normalRelativeAngle(absoluteBearing - getGunHeadingRadians()));

			}
			setTurnRadarRightRadians(radarTurn);
			enemies.get(e.getName()).setBearing(getHeadingRadians() + e.getBearingRadians());
		}

	}

	@Override
	public void onScannedRobot(ScannedRobotEvent e) {
		if (enemies.size() == 1 && getTime() > 20) {
			doRadar1vs1(e);
		}
		updateEnemy(e);
		if (!switched1vs1 && enemies.size() == 1) {
			enemies.get(e.getName()).waves.clear();
			switched1vs1 = true;
		}
		if (enemies.size() == 1) {
			processWaves1vs1(enemies.get(e.getName()));
		} else if (enemies.size() < Constants.SEMILINEAR_THRESHOLD)
			processWaves(enemies.get(e.getName()));
	}

	private void updateEnemy(ScannedRobotEvent e) {
		String name = e.getName();
		double absoluteBearing = getHeading() + e.getBearing();
		int direction = 1;
		if (e.getVelocity() != 0) {
			if (Math.sin(e.getHeadingRadians() - Math.toRadians(absoluteBearing)) * e.getVelocity() < 0)
				direction = -1;
			else
				direction = 1;
		}

		if (enemies.containsKey(name)) {
			enemies.get(name).setEnergy(e.getEnergy());
			enemies.get(name).setPosition(project(myPosition, absoluteBearing, e.getDistance()));
			enemies.get(name).setDistance(e.getDistance());
			enemies.get(name).setBearing(absoluteBearing);
			enemies.get(name).setHeading(e.getHeading());
			enemies.get(name).setSpeed(e.getVelocity());
			enemies.get(name).setDirection(direction);

		} else {
			Enemy enemy = new Enemy();
			enemy.setName(e.getName());
			enemy.setEnergy(e.getEnergy());
			enemy.setPosition(project(myPosition, absoluteBearing, e.getDistance()));
			enemy.setDistance(e.getDistance());
			enemy.setBearing(absoluteBearing);
			enemy.setHeading(e.getHeading());
			enemy.setSpeed(e.getVelocity());
			enemy.setDirection(direction);
			enemies.put(e.getName(), enemy);
		}
	}

	@Override
	public void onRobotDeath(RobotDeathEvent event) {
		enemies.remove(event.getName());
	}

	public static Point2D.Double project(Point2D.Double sourceLocation, double angle, double length) {
		angle = Math.toRadians(angle);
		Point2D.Double endPoint = new Point2D.Double();
		endPoint.x = sourceLocation.x + Math.sin(angle) * length;
		endPoint.y = sourceLocation.y + Math.cos(angle) * length;
		return endPoint;
	}

	public static Point2D.Double projectRadians(Point2D.Double sourceLocation, double angle, double length) {
		Point2D.Double endPoint = new Point2D.Double();
		endPoint.x = sourceLocation.x + Math.sin(angle) * length;
		endPoint.y = sourceLocation.y + Math.cos(angle) * length;
		return endPoint;
	}

	private double absoluteBearingRadians(Point2D source, Point2D target) {
		return Math.atan2(target.getX() - source.getX(), target.getY() - source.getY());
	}

	private double normalRelativeAngleRadians(double angle) {
		return Math.atan2(Math.sin(angle), Math.cos(angle));
	}

	private void goTo(Point2D point) {
		setTurnRightRadians(normalRelativeAngleRadians(absoluteBearingRadians(myPosition, point) - getHeadingRadians()));
		setAhead(myPosition.distance(point));
	}

	private double getBestFiringAngle(double[] firingAngles, int knnNumNeighbors, double distance) {
		double bestAngle = 0;
		double bestDensity = 0;
		double botWidthAngle = Math.abs(36 / distance);
		for (int i = 0; i < knnNumNeighbors; i++) {
			double density = 0;
			double currAngle_i = firingAngles[i];
			for (int j = 0; j < knnNumNeighbors; j++) {
				if (i != j) {
					double currAngle_j = firingAngles[j];
					double ux = (currAngle_i - currAngle_j) / botWidthAngle;
					if (Math.abs(ux) <= 1)// uniform
						density++;

					// gaussian
					// density = (1 / Math.sqrt(2 * PI)) * Math.exp(-0.5 *
					// Math.pow(ux, 2));
				}

				if (density > bestDensity)
					bestAngle = currAngle_i;
				bestDensity = density;
			}
		}
		return bestAngle;
	}

}
